#include "localsocket.hpp"
#include <sys/select.h>

namespace Local_Select
{

    class SelectServer
    {
        const static int _maxsize = sizeof(fd_set) * 8;
        const static int _defaultfd = -1;

    public:
        /**************构造析构************** */
        SelectServer(int port)
            : _listensock(std::make_unique<Local_Socket::TcpSocket>()),
              _isrunning(false)
        {
            _listensock->ServerTcpMod(port);
            for (int i = 0; i < _maxsize; i++)
            {
                _arrayfd[i] = _defaultfd;
            }
            _arrayfd[0] = _listensock->GetFd();
        }
        ~SelectServer() {}
        /**************成员函数************** */
        void Start()
        {
            _isrunning = true;
            while (_isrunning)
            {
                Local_Log::LOG(Local_Log::LogLever::LINFO) << "开始循环";

                // 因为: listensockfd,也是一个fd，进程怎么知道listenfd上面有新连接到来了呢？
                // auto res = _listensock->Accept(); // 我们在select这里，可以进行accept吗？
                // 将listensockfd添加到select内部，让OS帮我关心listensockfd上面的读事件

                // 1.定义fd_set
                fd_set lo_rfds;
                FD_ZERO(&lo_rfds); // 清空fds
                // 2.传输要监控的fd
                int lo_maxindex = _defaultfd;
                for (int i = 0; i < _maxsize; i++)
                {
                    // 过滤掉无效文件描述符
                    if (_arrayfd[i] == _defaultfd)
                        continue;
                    // 添加文件描述符
                    FD_SET(_arrayfd[i], &lo_rfds);
                    Local_Log::LOG(Local_Log::LogLever::LDEBUG) << "FD_SET add " << _arrayfd[i];
                    // 更新lo_maxindex
                    if (lo_maxindex < _arrayfd[i])
                        lo_maxindex = _arrayfd[i];
                    Local_Log::LOG(Local_Log::LogLever::LDEBUG) << "lo_maxindex " << lo_maxindex;
                }
                // Debug
                printarr(_arrayfd, _maxsize);
                // 3.select调用
                // struct timeval timeout = {0, 0};
                // select 返回之后，你怎么还知道哪些fd需要被添加到rfds，让select关心呢？
                // 所以：select要进行完整的设计，需要借助一个辅助数组！保存服务器历史获取过的所有的fd

                // rfds: 1111 1111
                // select负责事件就绪检测
                int ret_sel = select(lo_maxindex + 1, &lo_rfds, nullptr, nullptr, nullptr);
                // rfds: 0000 0000

                // 4.事件处理
                switch (ret_sel)
                {
                case -1:
                    Local_Log::LOG(Local_Log::LogLever::LERROR) << "select error ";
                    break;
                case 0:
                    Local_Log::LOG(Local_Log::LogLever::LINFO) << "select time out... ";
                    break;
                default:
                    Local_Log::LOG(Local_Log::LogLever::LDEBUG) << "select 监听到了事件... -> ret_sel: " << ret_sel;
                    Dispatcher(lo_rfds); // 处理就绪的事件啊！
                    break;
                }

                Local_Log::LOG(Local_Log::LogLever::LINFO) << "结束循环";
            }
            _isrunning = false;
        }
        void Stop()
        {
            _isrunning = false;
        }
        /**************事件函数************** */
        // 事件派发器
        void Dispatcher(fd_set &rfds)
        {
            for (int i = 0; i < _maxsize; i++)
            {
                // 过滤掉无效文件描述符
                if (_arrayfd[i] == _defaultfd)
                    continue;
                // fd合法，不一定就绪
                if (FD_ISSET(_arrayfd[i], &rfds))
                {
                    // fd_array[i] 上面一定是读就绪了
                    // listensockfd 新连接到来，也是读事件就绪啊
                    // sockfd 数据到来，读事件就绪啊
                    if (_arrayfd[i] == _listensock->GetFd())
                    {
                        // listensockfd 新连接到来
                        Accepter();
                    }
                    else
                    {
                        // 普通的读事件就绪
                        Recver(_arrayfd[i], i);
                    }
                }
            }
        }
        // 链接管理器
        void Accepter()
        {
            Local_Inetaddr::Inetaddr client;
            int lo_sockfd = _listensock->AcceptGetAndCreate(&client);
            if (lo_sockfd >= 0)
            {
                // 获取新链接到来成功, 然后呢？？能不能直接
                // read/recv()， sockfd是否读就绪，我们不清楚
                // 只有谁最清楚，未来sockfd上是否有事件就绪？select！
                // 将新的sockfd，托管给select！
                // 如何托管? 将新的fd放入辅助数组！

                Local_Log::LOG(Local_Log::LogLever::LINFO) << "accept success";

                int pos = 0;
                for (; pos < _maxsize; pos++)
                {
                    // 过滤掉无效文件描述符
                    if (_arrayfd[pos] == _defaultfd)
                        break;
                }
                // 如果pos > _maxsize代表，select监控满了
                if (pos >= _maxsize)
                {
                    // 关闭文件描述符，并结束
                    Local_Log::LOG(Local_Log::LogLever::LWARNING) << "select server full";
                    close(lo_sockfd);
                }
                else
                {
                    // 添加文件描述符给select
                    _arrayfd[pos] = lo_sockfd;
                }
            }
        }
        // IO处理器
        void Recver(int sockfd, int pos)
        {
            // 读取文件内容
            char lo_buffer[1024];
            int ret_rec = recv(sockfd, lo_buffer, sizeof(lo_buffer) - 1, 0);
            if (ret_rec > 0)
            {
                // 读取完成打印
                lo_buffer[ret_rec] = 0;
                std::cout << "client say@ " << lo_buffer << std::endl;
            }
            else if (ret_rec == 0)
            {
                // 文件描述符退出
                Local_Log::LOG(Local_Log::LogLever::LINFO) << "client exit";
                // 移除出数组
                _arrayfd[pos] = _defaultfd;
                close(sockfd);
            }
            else // if (ret_rec < 0)
            {
                // 读取错误
                Local_Log::LOG(Local_Log::LogLever::LERROR) << "recv error";
                // 移除出数组
                _arrayfd[pos] = _defaultfd;
                close(sockfd);
            }
        }
        /**************GetSet************** */
        /**************Debug************** */
        void printarr(int *arr, int len)
        {
            std::cout << "select fd:> ";
            for (int i = 0; i < _maxsize; i++)
            {
                if (_arrayfd[i] != _defaultfd)
                {
                    std::cout << _arrayfd[i] << " ";
                }
            }
            std::cout << std::endl;
        }

    private:
        std::unique_ptr<Local_Socket::Socket> _listensock;
        bool _isrunning;
        int _arrayfd[_maxsize];
    };

}